import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import IResponse from '@/services/response.service';
import * as puppeteer from "puppeteer";
import axios from 'axios';
import * as path from "path";
import * as fs from "fs-extra";
import IConstants from "@/core/constants";
import IHelper from "@/services/helper.service";
import {ConfigService} from "@nestjs/config";
import {ReqProvider} from "@/core/providers/req.provider";
import {
  concatMap,
  defer,
  of,
  tap,
  map,
  catchError,
  timeout,
  delay,
  ReplaySubject,
  Observable,
  from,
  TimeoutError,
} from 'rxjs';
import { IWonewSer } from '@/typing';
import IGlobal from '@/services/global.service';

@Injectable()
export class KsBindService {
  redirectUri: string = "https://cp.kuaishou.com/article/publish/video"
  authUri: string = "https://passport.kuaishou.com/pc/account/login/?sid=kuaishou.web.cp.api&callback=https%3A%2F%2Fcp.kuaishou.com%2Frest%2Finfra%2Fsts%3FfollowUrl%3Dhttps%253A%252F%252Fcp.kuaishou.com%252Farticle%252Fpublish%252Fvideo%26setRootDomain%3Dtrue"
  constructor(
    private readonly reqProvider: ReqProvider,
  ) {
  }
  prepare(accountId: number): Observable<IWonewSer.BindAccountTask> {
    const task: IWonewSer.BindAccountTask = {
      accountId,
      hash: IHelper.hash(24),
      startAt: Date.now(),
      state: 0,
    };
    IGlobal.bindAccountTaskSet[task.hash] = task;
    const webAjaxResponseSubject = new ReplaySubject<{type: "poll" | "generate", data: any}>()
    const loginButtonSelector = `#login-form > div > div > div.platform-switch.platform-switch-pc.default.svelte-irseqx`
    // @ts-ignore
    return defer(() => 
      puppeteer.launch({
        defaultViewport: {
          width: 1280,
          height: 720,
        },
        headless: true,
        // headless: false,
        args: ['--no-sandbox','--disable-setuid-sandbox'],
      }),
    ).pipe(
      tap((browser) => (task.browser = browser)),
      concatMap(() => task.browser.newPage()),
      tap((page) => (task.page = page)),
      concatMap(() => task.page.setRequestInterception(true)),
      map(() => {
        task.page.on('request', (interceptedRequest: puppeteer.HTTPRequest) => {
          if (interceptedRequest.isInterceptResolutionHandled()) return;
          if (
            interceptedRequest.url().endsWith('.png') ||
            interceptedRequest.url().endsWith('.jpg')
          ) {
            interceptedRequest.abort();
          } else {
            interceptedRequest.continue();
          }
        });
        task.page.on('response', async (res: puppeteer.HTTPResponse) => {
          const req: puppeteer.HTTPRequest = res.request()
          if (req.url().includes(`/infra/ks/qr/scanResult`)) {
            let resJson = await res.json().catch(e => undefined)
            if (resJson?.result === 1) {
              task.state = 1
            }
            if (resJson?.result == 707) {
              task.state = 2
            }
          }
          if (req.url().includes(`/infra/ks/qr/start`)) {
            let resJson = await res.json().catch(e => undefined)
            webAjaxResponseSubject.next({type: "generate", data: resJson})
          }
        });
      }),
      concatMap(() => task.page.goto(this.authUri)),
      concatMap(() => task.page.waitForSelector(loginButtonSelector)),
      concatMap(() => task.page.click(loginButtonSelector)),
      concatMap(() => {
        return new Observable<string>((observer => {
          webAjaxResponseSubject.subscribe(
            res => {
              if (res.type === "generate" && res.data?.qrUrl) {
                observer.next(res.data?.qrUrl)
                observer.complete()
              }
            }
          )
        }))
      }),
      tap(targeUri => task.targetUri = targeUri),
      map(() => task),
      timeout(15000),
      catchError(error => {
        task.browser?.close().catch(e => e)
        throw error
      })
    )
  }

  check(hash: string): Observable<IWonewSer.BindAccountTask> {
    return of(IGlobal.bindAccountTaskSet[hash]).pipe(
      map(task => {
        if (!task || task.state === 2) {
          throw new HttpException(`Hash expired.`, -2000)
        }
        return task
      }),
      concatMap(task => {
        if (task.page.url() !== this.redirectUri) {
          return of(task)
        }
        return from(task.page.cookies()).pipe(
          map(cookies => {
            task.browser.close().catch(e => e)
            task.cookie = JSON.stringify(cookies)
            IGlobal.bindAccountTaskSet[hash] = undefined
            return task
          }),
          concatMap(task => {
            return this.checkCookie(task.cookie).pipe(
              map(({platformUser}) => {
                if (!platformUser) {
                  throw new HttpException(`Get platform user by cookie failed.`, -2001)
                }
                task.state = 3
                task.platformUser = platformUser
                return task
              })
            )
          })
        ) 
      })
    )
  }

  checkCookie(cookie: string): Observable<{valid: boolean, platformUser?: any, cookie?: string}> {
    return defer(async () => {
      let browser: puppeteer.Browser | undefined = undefined
      try {
        browser = await puppeteer.launch({
          headless: true,
          args: ['--no-sandbox','--disable-setuid-sandbox'],
        })
        const page = await browser.newPage()
        const cookies = JSON.parse(cookie) as puppeteer.Cookie[]
        for(let i = 0; i < cookies.length; i++){
          await page.setCookie(cookies[i])
        }
        await page.goto("https://cp.kuaishou.com/article/publish/video")
        const userInfoRes = await page.waitForResponse(res => res.url().includes("authority/account/current"))
        const userInfoResJson = await userInfoRes.json()
        const updatedCookies = JSON.stringify(await page.cookies())
        await browser.close().catch(e => e)
        if (userInfoResJson.result === 1) {
          return {valid: true, platformUser: userInfoResJson.data, cookie: updatedCookies}
        }
        return {valid: false, platformUser: null, cookie: ""}
      } catch(e) {
        browser && browser.close().catch(e => e)
        throw new HttpException("Check bili cookie failed.", HttpStatus.BAD_REQUEST)
      }
    })
    const cookies: puppeteer.Protocol.Network.Cookie[] = JSON.parse(cookie)
    const api_ph = cookies.find(e => e.name === "kuaishou.web.cp.api_ph").value
    return defer(() => 
    axios.post(
      'https://cp.kuaishou.com/rest/v2/creator/pc/authority/account/current',
      {
        'kuaishou.web.cp.api_ph': api_ph
      },
      {
        headers: {
          'Accept': 'application/json',
          'Accept-Language': 'zh-CN,zh;q=0.9',
          'Cache-Control': 'no-cache',
          'Connection': 'keep-alive',
          'Content-Type': 'application/json;charset=UTF-8',
          'Cookie': IHelper.covert2StandardCookieStr(cookie),
          'Origin': 'https://cp.kuaishou.com',
          'Pragma': 'no-cache',
          'Referer': 'https://cp.kuaishou.com/article/publish/video',
          'Sec-Fetch-Dest': 'empty',
          'Sec-Fetch-Mode': 'cors',
          'Sec-Fetch-Site': 'same-origin',
          'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
          'returnSetRootDomainLoginUrl': 'true',
          'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
          'sec-ch-ua-mobile': '?0',
          'sec-ch-ua-platform': '"macOS"'
        }
      }
    )
    ).pipe(
      map(res => {
        return {
          valid: typeof res.data?.data?.userId === "number",
          platformUser: typeof res.data?.data?.userId === "number" ? res.data : undefined
        }
      })
    )
  }
}